/*
 * Created on 2005/04/26
 * Author aki@www.xucker.jpn.org
 * License Apache2.0 or Common Public License
 */
package jp.sourceforge.akjrcp.snack;

import org.eclipse.swt.SWT;
import org.eclipse.swt.graphics.ImageData;
import org.eclipse.swt.graphics.ImageLoader;
import org.eclipse.swt.graphics.PaletteData;
import org.eclipse.swt.graphics.RGB;


import org.apache.commons.lang.ArrayUtils;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.apache.tools.zip.ZipEntry;
import org.apache.tools.zip.ZipFile;
import org.apache.tools.zip.ZipOutputStream;
import jp.sourceforge.akjrcp.ui.ProgressMonitor;


import java.io.BufferedOutputStream;
import java.io.BufferedReader;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.io.Reader;
import java.util.ArrayList;

/**
 * 
 *
 */
public class SpectrogramGifConverter extends AbstractParser{
   
    private ProgressMonitor progressMonitor;
    //private int perProgress=50;//for progress monitor
    private int perMonitorCancel=50;
    private int samplerate;
    private int height;
    private double permillisecond;
    
    private boolean removeNoise=true;
    
    private PaletteData paletteData;
    
    int imageWidth=80;//default


    
   
    public static Log log=LogFactory.getLog(SpectrogramGifConverter.class);
    
    private boolean zippedFile=false;
    private String exportPath;
    private ZipOutputStream zipout;
    
    private int k=5;
    private SpectrogramListener spectrogramListener;
    
         
    private SpectrogramGifConverter(){
        
        //create gray colors
        RGB[] graycolors=new RGB[256];
        for(int i=0;i<256;i++){
            graycolors[i]=new RGB(i,i,i);
        }
        //0 is white
        graycolors[0]=new RGB(255,255,255);//
        paletteData=new PaletteData(graycolors);
    }
    
    public int getHeight() {
        return height;
    }
    public void setHeight(int height) {
        this.height = height;
    }
    public double getPermillisecond() {
        return permillisecond;
    }
    public void setPermillisecond(double permillisecond) {
        this.permillisecond = permillisecond;
    }
    public boolean isRemoveNoise() {
        return removeNoise;
    }
    public void setRemoveNoise(boolean removeNoise) {
        this.removeNoise = removeNoise;
    }
    public int getSamplerate() {
        return samplerate;
    }
    public void setSamplerate(int samplerate) {
        this.samplerate = samplerate;
    }
    
    public String toLabel(int index){
        String v=""+index;
        while(v.length()<k){
            v="0"+v;
        }
        return v;
    }
    public static int toValue256(double d){
        //
        if(d<0){
            d*=-1;
        }
        double r=Math.toRadians(d);
        double v=Math.sin(r)*1.1;//*1.1;
       // log.info"+r+","+v);
        /*
         * int c=0;
        c=Math.max(0,(int)((double)-(d-68)*2));
        c=Math.min(c,255);
        c=Math.max(0,c*c/256);
        return 255-c;*/
        
        return Math.min(255,(int)(v*256));
    }
   
   public SpectrogramGifConverter(int samplerate,int height,double permillisecond){
       this();
       this.samplerate=samplerate;
       this.height=height;
       this.permillisecond=permillisecond;
       init();
   }
   ArrayList<Integer> blist;
   
   NoiseRemover noiseRemover;
   
   
   
   public void init(){
      
       noiseRemover=new NoiseRemover(height);
   }
   
   
        public Object parse(Reader read) throws IOException{
            blist=new ArrayList();//todo mix?
            long spSize=0;
            //imageIndex=0;//not initialized becaouse of multi files.
            
            
            
            //log.info(this.getClass().getClassLoader().getResource("org/apache/tools/zip/ZipOutputStream.class"));
            if(zippedFile){
               
                zipout=new ZipOutputStream(new File(exportPath));
                zipout.setEncoding("SJIS");
                zipout.setLevel(0);
            }
            
            int index=0;
            //int tmpPer=perProgress*height;
            String line;
            BufferedReader reader=new  BufferedReader(read);
            int prev=0;
            int perpixel=imageWidth*height;
            
            
            //Image gifImage=new Image(display,imageWidth,height);
            ImageData imageData = new ImageData(imageWidth, height, 8, paletteData);
           
            //log.infostart-parse");
            //this version return diff of prev value.becouse of decrease data size.
            while((line=reader.readLine())!=null){
                //log.info(line);
                int v=0;
                try{
                    v=Integer.parseInt(line);
                    }catch(Exception e){
                    	//for number error};
                    }
                prev=prev-v;
                blist.add(toValue256(prev));
                if(progressMonitor!=null && index%perMonitorCancel==0){
                    if(progressMonitor.isCanceled()){
                        log.trace("parse-canceled");
                        reader.close();
                        return null;
                    }
                    
                	}
                index++;
                spSize++;
                if(index==perpixel){
                    int[] rawint=ArrayUtils.toPrimitive(blist.toArray(new Integer[blist.size()]));
                    if(removeNoise){
                        noiseRemover.removeNoize(rawint);
                    	}
                    for(int i=0;i<rawint.length;i++){
                        //gifImage.
                        imageData.setPixel(i/height,height-1-i%height,rawint[i]);
                    	}
                    saveImage(imageData);
                    index=0;
                    blist=new ArrayList();
                	}
            	}
            reader.close();
           
            if(blist.size()>0){
              
                	
                    int[] rawint=ArrayUtils.toPrimitive(blist.toArray(new Integer[blist.size()]));
                    if(removeNoise){
                        noiseRemover.removeNoize(rawint);
                    	}
                    for(int i=0;i<perpixel;i++){
                        //gifImage.
                        if(i<rawint.length){
                        imageData.setPixel(i/height,height-1-i%height,rawint[i]);
                        }else{
                            imageData.setPixel(i/height,height-1-i%height,0);//empty is 0
                        }
                    	}
                    saveImage(imageData);
            }
            
            //if zipsave.
            if(zipout!=null){
                zipout.close();
                zipout=null;
                spectrogramListener.imageSaved(exportPath);
            }
            //log.trace("size:"+blist.size());
            
            log.trace("spectrogram-length="+spSize/512);
            if(spSize>0){
                return new Integer((int)(spSize/512));
            }else{
                return new Integer(0);
            }
            
        }
        
        int imageIndex;
        //String imageDir; 
        ImageLoader loader=new ImageLoader();
        
        
        public void saveImage(ImageData imgData){
            
            loader.data=new ImageData[]{imgData};
            String label=toLabel(imageIndex);
            //???
            if(!zippedFile){
                String path=exportPath+File.separator+label+".gif";
            loader.save(path,SWT.IMAGE_GIF);
            log.trace("gif-image-save:"+path);
            if(spectrogramListener!=null){
                spectrogramListener.imageSaved(path);
            }
            }else{
                ZipEntry entry=new ZipEntry(label+".gif");
                try {
                    //
                    ByteArrayOutputStream bout=new ByteArrayOutputStream();
                    BufferedOutputStream buffer=new BufferedOutputStream(bout,10240);
                    log.info("createEntry:"+entry);
                   // log.infocreate entry:"+entry);
                    zipout.putNextEntry(entry);
                    loader.save(buffer,SWT.IMAGE_GIF);//becouse of flush?
                    
                    zipout.write(bout.toByteArray());
                    zipout.closeEntry();
                    
                    
                    //log.infoclose entry");
                } catch (IOException e) {
                    // TODO Auto-generated catch block
                    e.printStackTrace();
                }
                
            }
            imageIndex++;
            
            
            if(progressMonitor!=null){
                
                progressMonitor.worked(1);//monitor per image saved.
            }
        }
        /*
        public void  parseImage(Reader read) throws IOException{
            
            
            Spectrogram spectrogram=new Spectrogram();
            spectrogram.setHeight(height);
            spectrogram.setSamplerate(samplerate);
            spectrogram.setPerSample(permillisecond);
            
            int index=0;
            int tmpPer=perProgress*height;
            String line;
            BufferedReader reader=new  BufferedReader(read);
            int prev=0;
            //this version return diff of prev value.becouse of decrease data size.
            while((line=reader.readLine())!=null){
                int v=Integer.parseInt(line);
                prev=prev-v;//write diff values.
                blist.add(toSignedByte(toValue256(prev)));
                if(progressMonitor!=null && index%tmpPer==0){
                    if(progressMonitor.isCanceled()){
                        reader.close();
                        return ;
                    }
                    progressMonitor.worked(perProgress);//finished line.//ignore height
                	}
                index++;
            	}
            reader.close();
            int[] rawint=blist.toArray();
            spectrogram.setRawInt(rawint);
            
            for(int i=0;i<rawint.length;i++){
                rawint[i]=toValue256(rawint[i]);
            }
            
            if(removeNoise){
                //this is cheep.
                new NoiseRemover(height).removeNoize(spectrogram.getRawInt());
            }
            
            return spectrogram;
        }
        */
        public byte toSignedByte(int v){
            byte r=(byte)v;
            if(r>127){
                r-=256;
            }
            return r;
        }
        public static class NoiseRemover{
        private int sampleHeight;
        public NoiseRemover(int sampleheight){
            this.sampleHeight=sampleheight;
        }
        private void removeNoize(int[] values2) {
            int vsize=10;
            int x=values2.length/sampleHeight;
            
            for(int i=0;i<x;i++){
                int last[]=new int[vsize-1];
                for(int j=0;j<sampleHeight;j++){
                    int v=values2[i*sampleHeight+j];
                    if(v!=0){
                        if(isNear(v,last)){
                            //log.trace("remove:"+i+","+j);
                            
                            for(int k=0;k<last.length+1;k++){
                                values2[i*sampleHeight+j-k]=255;
                            }
                            
                            setInit(0,last);
                        }else{
                            setLast(v,last);
                        }
                    }else{
                        setLast(v,last);
                    }
                }
            }
            
        }
        
        /**
         * @param values2
         */
        private boolean near(int a,int b){
            int ch=2;
            return Math.abs(a-b)<=ch;
        }
       
        private boolean isNear(int v,int[] array){
            for(int i=array.length-1;i>0;i--){
                if(!near(array[i],array[i-1])){
                    return false;
                }
            }
            return near(array[0],v);
            
        }
        private void setInit(int value,int array[]){
            for(int i=0;i<array.length;i++){
                array[i]=value;
            }
        }
        private void setLast(int v,int array[]){
            for(int i=array.length-1;i>0;i--){
                array[i]=array[i-1];
            }
            array[0]=v;
        }
        }
    public ProgressMonitor getProgressMonitor() {
        return progressMonitor;
    }
    public void setProgressMonitor(ProgressMonitor progressMonitor) {
        this.progressMonitor = progressMonitor;
    }
       
    public int getImageWidth() {
        return imageWidth;
    }
    public void setImageWidth(int imageWidth) {
        this.imageWidth = imageWidth;
    }
    public String getExportPath() {
        return exportPath;
    }
    public void setExportPath(String exportPath) {
        this.exportPath = exportPath;
    }
    public boolean isZippedFile() {
        return zippedFile;
    }
    public void setZippedFile(boolean zippedFile) {
        this.zippedFile = zippedFile;
    }
    public SpectrogramListener getSpectrogramListener() {
        return spectrogramListener;
    }
    public void setSpectrogramListener(SpectrogramListener spectrogramListener) {
        this.spectrogramListener = spectrogramListener;
    }
}
